#ifndef CAMERA_H
#define CAMERA_H

#include "GL/glew.h"
#include "GL/glfw.h"
#include "Common/Macro.h"
#include "Common/Output.h"
#include "Maths/Math.h"
#include "Maths/Matrix.h"
#include "Maths/Quaternion.h"

//#include "Maths/glm/glm.hpp"

class Camera
{
public:
	Camera(void);
	virtual ~Camera(void);
	void Init();
	inline void Update();
	inline void LookAt(const Vec3& aTarget);
	inline void SetPosition(const Vec3& aPos);
	inline void SetUpVector(const Vec3& aPos);

	inline void MoveForward(float aDist, bool aMoveTarget);
	inline void MoveRigth(float aDist, bool aMoveTarget);
	inline void MoveUp(float aDist, bool aMoveTarget);

	inline Matrix& GetViewMatrix();
// 	inline const Matrix& GetProjectionMatrix() const;
// 	inline Matrix& GetProjectionMatrix();

private:
	// Quaternion InternalLookAt(const Vec3& aTarget, const Vec3& aCurrentLook, const Vec3& aCamPos, const Vec3& aUpVector);
	void ComputeViewMat();
	//void InitProjectionMat(float FOV, float AspectRatio, float zNear, float zFar);

private:

	Vec3 mPos;
	Vec3 mTarget;
	Vec3 mUp;

	bool mDirty;

	Matrix mViewMatrix;
	//Matrix mProjectionMatrix;
};


inline Matrix& Camera::GetViewMatrix()
{ 
	Update();
	return mViewMatrix; 
}
// inline const Matrix& Camera::GetProjectionMatrix() const 
// {
// 	return mProjectionMatrix; 
// }
// inline Matrix& Camera::GetProjectionMatrix()
// {
// 	return mProjectionMatrix; 
// }

inline void Camera::LookAt(const Vec3& aTarget)
{
	mDirty = true;
	mTarget.Set(aTarget.X(), aTarget.Y(), aTarget.Z());
}
inline void Camera::SetPosition(const Vec3& aPos)
{
	mDirty = true;
	mPos.Set(aPos.X(), aPos.Y(), aPos.Z());
}
inline void Camera::SetUpVector(const Vec3& aUpVector)
{
	mDirty = true;
	mUp.Set(aUpVector.X(), aUpVector.Y(), aUpVector.Z());
}


inline void Camera::MoveForward(float aDist, bool aMoveTarget)
{
	Vec3 N = mTarget - mPos;
	N.Normalize();
	N.SelfScale(aDist);
	mPos += N ;

	if (aMoveTarget)
		mTarget += N;

	float tx = mPos.X();
	float ty = mPos.Y();
	float tz = mPos.Z();
	Output::Printf("x:%.2f, y:%.2f, z:%.2f \n", tx, ty, tz);
	mDirty = true;
}
inline void Camera::MoveRigth(float aDist, bool aMoveTarget)
{
	Vec3 N = mTarget - mPos;
	N.Normalize();
	
	Vec3 U = N.Cross(mUp);
	U.SelfScale(aDist);
	mPos += U ;

	if (aMoveTarget)
		mTarget += U;

	//Output::Printf("x:%f, y:%f, z:%f \n", mPos.X(), mPos.Y(), mPos.Z());
	float tx = mPos.X();
	float ty = mPos.Y();
	float tz = mPos.Z();
	Output::Printf("x:%.2f, y:%.2f, z:%.2f \n", tx, ty, tz);

	mDirty= true;
}
inline void Camera::MoveUp(float aDist, bool aMoveTarget)
{

}

void Camera::Update()
{
	if (mDirty)
	{
		ComputeViewMat();
	}
}

#endif // Camera_H

